﻿using System.Collections.Generic;
using System.Data;
using Azalea.Common;

namespace Azalea.ORMap
{
    public class CommandExecutor : ICommandExecutor
    {
        private readonly Dictionary<IDbCommand, IDbConnection> _cmdConnections =
            new Dictionary<IDbCommand, IDbConnection>();

        private readonly IDbContext _dbContext;

        public CommandExecutor(IDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        #region ICommandExecutor Members

        public DataSet DataSetExecute(IDbCommand cmd)
        {
            IDataAdapter da = _dbContext.DbFactory.NewDataAdapter(cmd);
            var ds = new DataSet();

            SetConnectionToCmdIfNeed(cmd);
            da.Fill(ds);
            CloseConnectionOfCmdIfNeed(cmd);

            return ds;
        }

        public DataTable DataTableExecute(IDbCommand cmd)
        {
            DataSet ds = DataSetExecute(cmd);
            if (ds.Tables.Count == 1)
            {
                return ds.Tables[0];
            }
            throw new AzaleaException("DataTableExecute returns more than one table");
        }

        public DataRow DataRowExecute(IDbCommand cmd)
        {
            DataTable dt = DataTableExecute(cmd);
            if (dt.Rows.Count == 1)
            {
                return dt.Rows[0];
            }
            if (dt.Rows.Count > 1)
            {
                throw new AzaleaException("DataRowExecute returns more than one table");
            }
            return null;
        }

        public int NonQueryExecute(IDbCommand cmd)
        {
            SetConnectionToCmdIfNeed(cmd);
            int affectedRows = cmd.ExecuteNonQuery();
            CloseConnectionOfCmdIfNeed(cmd);

            return affectedRows;
        }

        public object ScalarExecute(IDbCommand cmd)
        {
            SetConnectionToCmdIfNeed(cmd);
            object affectedRows = cmd.ExecuteScalar();
            CloseConnectionOfCmdIfNeed(cmd);

            return affectedRows;
        }

        #endregion

        private void SetConnectionToCmdIfNeed(IDbCommand cmd)
        {
            if (cmd != null && cmd.Connection == null)
            {
                cmd.Connection = _dbContext.OpenConnection(true);
                _cmdConnections.Add(cmd, cmd.Connection);
            }
        }

        private void CloseConnectionOfCmdIfNeed(IDbCommand cmd)
        {
            if (cmd != null && _cmdConnections.ContainsKey(cmd))
            {
                _dbContext.CloseConnection(_cmdConnections[cmd]);
                _cmdConnections.Remove(cmd);
            }
        }
    }
}